package com.ls.demo.bean;
import	java.io.Serializable;

import com.ls.demo.enums.CommandTypeEnum;
import com.ls.demo.enums.MessageAttrEnum;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;

/**
 * 命令
 * @author liShan
 * @date 2019/8/30 22:03
 */
@Getter
@Setter
public class Command implements Serializable, Cloneable {

    /**
     * 包头
     * @see CommandTypeEnum
     */
    private String header;

    /**
     * 内容长度，为当前位置后面的位数，包括当前位置
     */
    private String length;

    /**
     * 目标地址，高位
     */
    private String tarAddrHigh;

    /**
     * 目标地址，低位
     */
    private String tarAddrLow;

    /**
     * 源地址，高位
     */
    private String srcAddrHigh;

    /**
     * 源地址，低位
     */
    private String srcAddrLow;

    /**
     * 数据长度：为报文属性和数据长度之和
     */
    private String dataLong;

    /**
     * 报文属性
     * @see MessageAttrEnum
     */
    private String messageAttr;

    /**
     * 数据
     */
    private List<String> dataList;

    /**
     * 栋号
     */
    private String buildingNum;

    /**
     * 酒店号高位
     */
    private String hotelNumHigh;

    /**
     * 酒店号低位
     */
    private String hotelNumLow;

    /**
     * 异或效验
     */
    private String ver;

    /**
     * 结尾
     * @see CommandTypeEnum
     */
    private String tail;

    public Command() {
    }

    private Command(Builder builder){
        this.header = builder.header;
        this.length = builder.length;
        this.tarAddrHigh = builder.tarAddrHigh;
        this.tarAddrLow = builder.tarAddrLow;
        this.srcAddrHigh = builder.srcAddrHigh;
        this.srcAddrLow = builder.srcAddrLow;
        this.dataLong = builder.dataLong;
        this.messageAttr = builder.messageAttr;
        this.dataList = builder.dataList;
        this.buildingNum = builder.buildingNum;
        this.hotelNumHigh = builder.hotelNumHigh;
        this.hotelNumLow = builder.hotelNumLow;
        this.ver = builder.ver;
        this.tail = builder.tail;
    }

    /**
     * 根据命令生成一个命令对象
     * @param command  命令
     * @return 命令对象
     */
    public static Command getCommand(String command){
        String[] str = command.split("\\s");
        int max = str.length - 5;
        List<String> dataList = Arrays.asList(Arrays.copyOfRange(str, 8, max));
        return new Builder().header(str[0])
                .length(str [1])
                .tarAddrHigh(str [2])
                .tarAddrLow(str [3])
                .srcAddrHigh(str [4])
                .srcAddrLow(str [5])
                .dataLong(str [6])
                .messageAttr(str [7])
                .dataList(dataList)
                .buildingNum(str [str.length - 5])
                .hotelNumHigh(str [str.length - 4])
                .hotelNumLow(str [str.length - 3])
                .ver(str [str.length - 2])
                .tail(str [str.length - 1]).build();
    }

    /**
     * 生成命令
     * @return 命令
     */
    public String getCommand(){
        return this.getCommandVal(null);
    }

    private String getCommandVal(String valCode){
        StringBuilder sb = new StringBuilder();
        sb.append(this.header).append(" ")
                .append(this.length).append(" ")
                .append(this.tarAddrHigh).append(" ")
                .append(this.tarAddrLow).append(" ")
                .append(this.srcAddrHigh).append(" ")
                .append(this.srcAddrLow).append(" ")
                .append(this.dataLong).append(" ")
                .append(this.messageAttr).append(" ");
        Optional.ofNullable(this.dataList).ifPresent(
                ds -> ds.forEach(d -> sb.append(d).append(" "))
        );
        sb.append(this.buildingNum).append(" ")
                .append(this.hotelNumHigh).append(" ")
                .append(this.hotelNumLow).append(" ");
        if (StringUtils.isNotBlank(valCode)){
            sb.append(valCode).append(" ");
        }else {
            sb.append(this.ver).append(" ");
        }
        sb.append(this.tail).append(" ");
        return sb.toString();
    }

    /**
     * 获取本对象中的长度值
     * @return 长度，是16进制值
     */
    public String getLengthHex(){
        return Integer.toHexString(this.getLengthDec());
    }

    /**
     * 获取本对象中的长度值
     * @return 长度，是10进制值
     */
    public int getLengthDec(){
        return null == dataList ? 12 : 12 + dataList.size();
    }

    /**
     * 获取本对象中的数据长度值
     * @return 长度，是16进制值
     */
    public String getDataLongHex(){
        return Integer.toHexString(this.getDataLongDec());
    }

    /**
     * 获取本对象中的数据长度值
     * @return 长度，是10进制值
     */
    public int getDataLongDec(){
        return null == dataList ? 1 : 1 + dataList.size();
    }

    /**
     * 获取校验位
     * @return 返回16进制字符串
     */
    public String getValCode(){
        int iChecked = 0;
        iChecked = iChecked ^  Integer.parseInt(this.length, 16)
                ^  Integer.parseInt(this.tarAddrHigh, 16)
                ^  Integer.parseInt(this.tarAddrLow, 16)
                ^  Integer.parseInt(this.srcAddrHigh, 16)
                ^  Integer.parseInt(this.srcAddrLow, 16)
                ^  Integer.parseInt(this.dataLong, 16)
                ^  Integer.parseInt(this.messageAttr, 16);
        for (String str : dataList) {
            iChecked ^= Integer.parseInt(str, 16);
        }
        iChecked = iChecked ^ Integer.parseInt(this.buildingNum, 16)
                ^ Integer.parseInt(this.hotelNumHigh, 16)
                ^ Integer.parseInt(this.hotelNumLow, 16);
        return Integer.toHexString(iChecked).toUpperCase();
    }

    /**
     * 重新生成校验位的命令
     * @return 命令
     */
    public String getCommandVal(){
        String valCode = this.getValCode();
        return this.getCommandVal(valCode);
    }

    /**
     * 获取十进制的酒店号
     * @return 十进制的酒店号
     */
    public int getHotelDec(){
        return (Integer.parseInt(this.hotelNumHigh, 16) << 8)
                | Integer.parseInt(this.hotelNumLow, 16);
    }

    /**
     * 获取十进制的栋号号
     * @return 十进制的栋号号
     */
    public int getBuildDec(){
        return Integer.parseInt(this.buildingNum, 16);
    }

    /**
     * 获取十进制的楼层号
     * @return 十进制的楼层号
     */
    public int getLayerDec(){
        return Integer.parseInt(this.srcAddrHigh, 16);
    }

    /**
     * 获取十进制的房间号
     * @return 十进制的房间号
     */
    public int getRoomDec(){
        return Integer.parseInt(this.srcAddrLow, 16);
    }

    @Override
    public Command clone() throws CloneNotSupportedException {
        return (Command)super.clone();
    }

    public static class Builder{

        private String header;
        private String length;
        private String tarAddrHigh;
        private String tarAddrLow;
        private String srcAddrHigh;
        private String srcAddrLow;
        private String dataLong;
        private String messageAttr;
        private List<String> dataList;
        private String buildingNum;
        private String hotelNumHigh;
        private String hotelNumLow;
        private String ver;
        private String tail;

        public Builder header(String header) {
            this.header = header;
            return this;
        }

        public Builder length(String length) {
            this.length = length;
            return this;
        }

        public Builder tarAddrHigh(String tarAddrHigh) {
            this.tarAddrHigh = tarAddrHigh;
            return this;
        }

        public Builder tarAddrLow(String tarAddrLow) {
            this.tarAddrLow = tarAddrLow;
            return this;
        }

        public Builder srcAddrHigh(String srcAddrHigh) {
            this.srcAddrHigh = srcAddrHigh;
            return this;
        }

        public Builder srcAddrLow(String srcAddrLow) {
            this.srcAddrLow = srcAddrLow;
            return this;
        }

        public Builder dataLong(String dataLong) {
            this.dataLong = dataLong;
            return this;
        }

        public Builder messageAttr(String messageAttr) {
            this.messageAttr = messageAttr;
            return this;
        }

        public Builder dataList(List<String> dataList) {
            this.dataList = dataList;
            return this;
        }

        public Builder buildingNum(String buildingNum) {
            this.buildingNum = buildingNum;
            return this;
        }

        public Builder hotelNumHigh(String hotelNumHigh) {
            this.hotelNumHigh = hotelNumHigh;
            return this;
        }

        public Builder hotelNumLow(String hotelNumLow) {
            this.hotelNumLow = hotelNumLow;
            return this;
        }

        public Builder ver(String ver) {
            this.ver = ver;
            return this;
        }

        public Builder tail(String tail) {
            this.tail = tail;
            return this;
        }

        public Command build() {
            return new Command(this);
        }

    }

}
